package com.liaowei.article.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.liaowei.article.mapper.ArticleMapper;
import com.liaowei.article.mapper.ArticleTagMapper;
import com.liaowei.article.mapper.ArticleUserMapper;
import com.liaowei.article.model.param.ArticleStatusParam;
import com.liaowei.article.model.param.QueryManageArticleParam;
import com.liaowei.article.model.param.SaveArticleParam;
import com.liaowei.article.openfeign.ArticleCommentClient;
import com.liaowei.article.openfeign.TagClient;
import com.liaowei.article.openfeign.UserClient;
import com.liaowei.article.service.ArticleService;
import com.liaowei.common.BaseResponse;
import com.liaowei.common.Constants;
import com.liaowei.common.ErrorCode;
import com.liaowei.common.RedisConstants;
import com.liaowei.exception.CustomerException;
import com.liaowei.model.domain.Article;
import com.liaowei.model.domain.ArticleTag;
import com.liaowei.model.domain.ArticleUser;
import com.liaowei.model.domain.Tag;
import com.liaowei.model.param.QueryArticleParam;
import com.liaowei.model.vo.ArticleVO;
import com.liaowei.model.vo.UserVO;
import com.liaowei.model.vo.WordCloud;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author LindaMan
 * @description 针对表【wy_article(文章表)】的数据库操作Service实现
 * @createDate 2023-05-06 23:31:37
 */
@Service
@RequiredArgsConstructor
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article>
        implements ArticleService {

    private final static  String SENSITIVE_TITLE = "0";
    private final static  String SENSITIVE_DESCRIPTION = "1";
    private final ArticleMapper articleMapper;
    private final ArticleTagMapper articleTagMapper;
    private final ArticleUserMapper articleUserMapper;
    private final TagClient tagClient;
    private final UserClient userClient;
    private final ArticleCommentClient articleCommentClient;
    private final RedisTemplate redisTemplate;
    private final DataSourceTransactionManager transactionManager;
    @Value("${python.path.minHash}")
    private String minHashPath;
    @Value("${python.path.sensitive}")
    private String sensitivePath;
    @Value("${python.path.ocr}")
    private String ocrPath;
    @Value("${python.path.ContentOcr}")
    private String ContentOcrPath;


    // 校验用户是否认证
    public void matchUser(Long userId, Long loginId) {
        if (!loginId.equals(userId)) {
            throw new CustomerException(Constants.PARAM_ERROR, "请先认证");
        }
    }

    // 校验标签是否存在
    public void matchTag(List<Long> tagIdList) {
        BaseResponse<Integer> response = tagClient.countTagByIds(tagIdList);
        if (response.getCode() == 1) {
            if (tagIdList.size() != response.getData())
                throw new CustomerException(Constants.PARAM_ERROR, "未查询到有效标签");
        }
    }

    // 根据标签id集合获取文章标签列表
    private static List<ArticleTag> getArticleTagListByTagIds(Long createBy, Long updateBy, List<Long> tagIdList, Article article) {
        // 封装articleTag
        return tagIdList.stream().map(tagId -> {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setArticleId(article.getId());
            articleTag.setTagId(tagId);
            articleTag.setCreateBy(createBy);
            articleTag.setUpdateBy(updateBy);
            return articleTag;
        }).collect(Collectors.toList());
    }

    // article转换为articleVO
    public ArticleVO toArticleVO(Article article) {
        Long articleId = article.getId(); // 获取articleId
        // 封装ArticleVO
        ArticleVO articleVO = new ArticleVO();
        BeanUtil.copyProperties(article, articleVO);
        // 封装其他属性
        ArticleVO temp = articleMapper.selectArticleVOById(articleId);
        if (BeanUtil.isNotEmpty(temp)) {
            articleVO.setAuthor(temp.getAuthor());
            articleVO.setTagList(temp.getTagList());
            articleVO.setStar(temp.getStar());
            articleVO.setAccessNumber(temp.getAccessNumber());
        }
        // 封装commentList，无需封装只需要前端调用文章评论服务的接口即可

        return articleVO;
    }

    // 查询所有文章内容
    public List<String> getAllArticleContent(Long articleId) {
        if (BeanUtil.isEmpty(articleId)) {
            // 文章id为空，新增文章，查询所有的文章内容
            return Optional.of(articleMapper.selectList(null).stream().map(Article::getContent)
                    .collect(Collectors.toList())).orElse(new ArrayList<>());
        } else {
            // 文章id不为空，修改文章，查询其余的的文章内容
            return Optional.of(articleMapper.selectList(Wrappers.lambdaUpdate(Article.class)
                            .notIn(Article::getId, articleId)).stream().map(Article::getContent)
                    .collect(Collectors.toList())).orElse(new ArrayList<>());
        }
    }

    // 相似度匹配
    public boolean calculateSimilarity(Long id) {
        String program = this.minHashPath; // 代码物理位置
        // 调用python代码
        Process proc = null;
        BufferedReader in = null;
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("python", program, id.toString());
            processBuilder.redirectErrorStream(true);
            proc = processBuilder.start();
            in = new BufferedReader(new InputStreamReader(proc.getInputStream(), "GBK"));
            String line;
            StringBuilder result = new StringBuilder();
            // 接收返回结果
            while ((line = in.readLine()) != null) {
                result.append(line);
            }

            in.close();
            return !result.toString().contains("weiy=");
        } catch (IOException e) {
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }
    }

    // 屏蔽敏感词
    public String maskSensitiveWords(Long articleId, String type) {
        String program = this.sensitivePath; // 代码物理位置
        Process proc = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("python", program, articleId.toString(), type);
            processBuilder.redirectErrorStream(true);
            proc = processBuilder.start();
            in = new BufferedReader(new InputStreamReader(proc.getInputStream(), "GBK"));
            String line;
            // 接收返回结果
            while ((line = in.readLine()) != null) {
                result.append(line);
            }

            in.close();
        } catch (Exception e) {
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }
        String[] split = result.toString().split("weiy="); // 与python中约定的分割字符串
        return split[split.length - 1]; // 不为空则认为相似
    }

    // 屏蔽内容及内容图片敏感信息
    public String callContentAndImageSensitivePython(String program, Long articleId) {
        Process proc = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("python", program, articleId.toString());
            processBuilder.redirectErrorStream(true);
            proc = processBuilder.start();
            in = new BufferedReader(new InputStreamReader(proc.getInputStream(), "GBK"));
            String line;
            // 接收返回结果
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            in.close();
        } catch (Exception e) {
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }
        String[] split = result.toString().split("weiy="); // 与python中约定的分割字符串
        return split[split.length - 1];
    }

    // 屏蔽封面图敏感信息
    public String callCoverImageSensitivePython(String program, String param) {
        Process proc = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("python", program, param);
            processBuilder.redirectErrorStream(true);
            proc = processBuilder.start();
            in = new BufferedReader(new InputStreamReader(proc.getInputStream(), "GBK"));
            String line;
            // 接收返回结果
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            in.close();
        } catch (Exception e) {
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }
        return result.toString();
    }

    // 屏蔽敏感图片
    public Article maskSensitiveConetntAndImg(Article article) {
        String coverImage = article.getCoverImage(); // 获取封面图片
        String content = article.getContent(); // 获取内容
        Gson coverGson = new Gson();

        // 封面图片检测
        if (StrUtil.isNotBlank(coverImage)) {
            String coverStr = callCoverImageSensitivePython(this.ocrPath, coverImage);
            // 包含敏感信息
            Map<String, String> resultMap = coverGson.fromJson(coverStr, new TypeToken<Map<String, String>>() {
            }.getType());
            if (resultMap != null) {
                Set<Map.Entry<String, String>> entries = resultMap.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    if (!entry.getKey().equals(entry.getValue())) {
                        article.setCoverImage("");
                    }
                }
            }
        }

        if (StrUtil.isNotBlank(content)) {
            // 内容、图片检测
            String contentStr = callContentAndImageSensitivePython(this.ContentOcrPath, article.getId());
            article.setContent(contentStr);
        }

        return article;
    }


    /**
     * 查询搜索发现--随机取10条
     *
     * @param title 搜索标题
     * @return List<Article>
     */
    @Override
    public List<Article> searchDiscoveryArticleList(String title) {
        if (title == null)
            title = "";
        List<Article> articleList = articleMapper.selectDiscoveryArticleList(title);

        return Optional.ofNullable(articleList).orElse(new ArrayList<>());
    }

    /**
     * 查询推荐文章
     *
     * @return List<ArticleVO>
     */
    @Override
    public List<ArticleVO> searchRecommendArticleVOList() {
        // 判断用户是否登录
        long userId = StpUtil.getLoginIdAsLong();
        if (BeanUtil.isEmpty(userId)) {
            // 用户未登录 不需要推荐
            return new ArrayList<>();
        }

        // 查询用户兴趣标签
        BaseResponse<List<Tag>> response = userClient.interestTagList(userId);
        if (response.getCode() == 1) {
            // 有兴趣标签
            List<Tag> interesTagList = response.getData();
            // 判断是否具有用户标签
            if (!interesTagList.isEmpty()) {
                // 有兴趣标签，通过兴趣标签来推荐
                // 根据兴趣标签id查询文章id
                LambdaQueryWrapper<ArticleTag> wrapper = Wrappers.lambdaQuery(ArticleTag.class)
                        .in(ArticleTag::getTagId, interesTagList);
                List<ArticleTag> articleTagList = articleTagMapper.selectList(wrapper);
                List<Long> articleIdList = articleTagList.stream().map(ArticleTag::getArticleId).collect(Collectors.toList());
                // 根据文章id查询文章列表
                if (!articleIdList.isEmpty()) {
                    List<Article> articleList = articleMapper.selectBatchIds(articleIdList);
                    // 转换VO
                    return articleList.stream().filter(v -> v.getArticleStatus() == 1).map(this::toArticleVO).collect(Collectors.toList());
                }
            }
        }

        // 没有兴趣标签或者其对应的文章，就查询全部的文章
        return articleMapper.selectList(Wrappers.lambdaQuery(Article.class).orderByDesc(Article::getCreateTime))
                .stream().filter(v -> v.getArticleStatus() == 1).map(this::toArticleVO).collect(Collectors.toList());
    }

    /**
     * 查询自己的文章
     *
     * @param userId 用户id
     * @param title  搜索标题
     * @return List<ArticleVO>
     */
    @Override
    public List<ArticleVO> searchOwnArticleVOList(Long userId, String title) {
        // 检查用户是否合法
        long loginId = StpUtil.getLoginIdAsLong();
//        matchUser(userId,loginId);

        // 判断是否是查询自己的文章
        Integer status = null;
        if (loginId != userId) {
            status = 1; // 不是查询自己的文章就只查询状态为审核成功的
        }

        // 构造查询构造器
        LambdaQueryWrapper<Article> wrapper = Wrappers.lambdaQuery(Article.class).like(Article::getTitle, title)
                .eq(Article::getUserId, userId).eq(status != null, Article::getArticleStatus, status)
                .orderByAsc(Article::getArticleStatus).orderByDesc(Article::getCreateTime);

        // 查询文章
        List<Article> articleList = articleMapper.selectList(wrapper);

        // 转换VO
        return articleList.stream().map(this::toArticleVO).collect(Collectors.toList());
    }

    /**
     * 查询文章热门排行
     *
     * @return List<ArticleVO>
     */
    @Override
    public List<ArticleVO> searchHotArticleList() {
        return Optional.ofNullable(articleMapper.selectHotArticleList())
                .orElse(new ArrayList<>());
    }

    /**
     * 查询热点搜索词
     *
     * @return List<WordCloud>
     */
    @Override
    public Set<WordCloud> searchWordCloudList() {
        Set<ZSetOperations.TypedTuple<String>> wordCloudSet = redisTemplate
                .opsForZSet().reverseRangeWithScores(RedisConstants.WORD_CLOUD_KEY, 0, -1);
        Set<WordCloud> set = new HashSet<>();
        for (ZSetOperations.TypedTuple<String> tuple : wordCloudSet) {
            WordCloud wordCloud = new WordCloud();
            wordCloud.setName(tuple.getValue());
            wordCloud.setValue(tuple.getScore());
            set.add(wordCloud);
        }
        return set;
    }

    /**
     * 通过id列表查询文章列表
     *
     * @param articleIds 文章id列表
     * @return List<Article> 文章列表
     */
    @Override
    public List<Article> searchArticleListByIds(List<Long> articleIds) {
        return Optional.ofNullable(this.listByIds(articleIds)).orElse(new ArrayList<>());
    }

    /**
     * 根据userId查询文章数量
     *
     * @param userId 用户id
     * @return 总条数
     */
    @Override
    public Long searchCountOfArticleByUserId(Long userId) {
        return articleMapper.selectCount(Wrappers.lambdaQuery(Article.class).eq(Article::getUserId, userId));
    }

    /**
     * 删除文章
     *
     * @param articleId 文章id
     * @param userId    用户id
     * @return 提示信息
     */
    @Override
    public String removeArticleByIdAndUserId(Long articleId, Long userId) {
        // 校验用户
        if (userId != 1) {
            // 不是管理员就需要校验是否认证
            Long loginId = Long.parseLong(StpUtil.getLoginId().toString());
            matchUser(userId, loginId);
        }

        try {
            // 删除文章用户
            LambdaUpdateWrapper<ArticleUser> deleteAUWrapper = Wrappers.lambdaUpdate(ArticleUser.class)
                    .eq(ArticleUser::getArticleId, articleId);
            articleUserMapper.delete(deleteAUWrapper);

            // 删除评论
            articleCommentClient.deleteComment(articleId, userId);

            // 删除用户的浏览文章记录
            userClient.deleteArticleHistoryByUserIdAndArticleId(userId, articleId);

            // 删除文章标签
            LambdaUpdateWrapper<ArticleTag> deleteATWrapper = Wrappers.lambdaUpdate(ArticleTag.class)
                    .eq(ArticleTag::getArticleId, articleId);
            articleTagMapper.delete(deleteATWrapper);

            // 删除文章
            articleMapper.deleteById(articleId);
        } catch (Exception e) {
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }

        return "删除成功";
    }

    /**
     * 根据id查询文章信息
     *
     * @param articleId 文章id
     * @return 文章信息
     */
    @Override
    public Article searchArticleById(Long articleId) {
        Article article = articleMapper.selectById(articleId);
        /*if (BeanUtil.isEmpty(article) || article.getStatus() != 1) {
            throw new CustomerException(Constants.PARAM_ERROR, "请输入合法id");
        }*/
        return articleMapper.selectById(articleId);
    }

    /**
     * 查询文章详细信息
     *
     * @param articleId 文章id
     * @return ArticleVO
     */
    @Override
    public ArticleVO searchArticleVo(Long articleId, Long userId) {
        // 根据id查询文章
        Article article = articleMapper.selectById(articleId);

        // 判断文章是否存在
        if (BeanUtil.isEmpty(articleId))
            throw new CustomerException(Constants.PARAM_ERROR, "不存在该文章");

        // 封装ArticleVO
        ArticleVO articleVO = toArticleVO(article);
        // 额外封装参数
        // collectStatus
        Integer collectStatus = articleUserMapper.selectCollectStatus(articleId, userId);
        articleVO.setCollectStatus(collectStatus);
        // starStatus
        Integer starStatus = articleUserMapper.selectStarStatus(articleId, userId);
        articleVO.setStarStatus(starStatus);

        // 返回结果
        return articleVO;
    }

    /**
     * 条件分页查询文章列表
     *
     * @param articleParam 分页查询文章参数
     * @return IPage<ArticleVO>
     */
    @Override
    public IPage<ArticleVO> searchArticlePage(QueryArticleParam articleParam) {
        // 获取搜索的标题
        String searchTitle = articleParam.getTitle();
        // 判断搜索关键字是否为空
        if (StrUtil.isNotBlank(searchTitle)) {
            // 查询redis中是否有搜索关键字
            if (redisTemplate.hasKey(RedisConstants.WORD_CLOUD_KEY)) {
                // 存在key
                // 搜索频次加1
                redisTemplate.opsForZSet().incrementScore(RedisConstants.WORD_CLOUD_KEY, searchTitle, 1);
            } else {
                // 不存在key
                // 添加元素
                redisTemplate.opsForZSet().add(RedisConstants.WORD_CLOUD_KEY, searchTitle, 1);
            }
        }

        // 分页数据处理
        int currentPage = articleParam.getPageParam().getPageNum();
        int pageSize = articleParam.getPageParam().getPageSize();
        IPage<ArticleVO> page = new Page<>(currentPage, pageSize);
        int pageNum = (currentPage - 1) * pageSize;
        articleParam.getPageParam().setPageNum(pageNum);

        // 分页查询
        List<ArticleVO> articleVOList = null;
        Integer queryType = articleParam.getType(); // 最新 | 热门。 默认 0-最新 1-热门
        int count = 0;
        if (queryType == Constants.QUERY_ARTICLE_TYPE_NEW) {
            // 最新查询
            articleVOList = Optional.ofNullable(articleMapper.selectNewArticlePage(articleParam))
                    .orElse(new ArrayList<>());
            // 查询总条数
            count = articleMapper.selectCountNewArticlePage(articleParam);

        } else if (queryType == Constants.QUERY_ARTICLE_TYPE_HOT) {
            // todo 最热查询

        } else {
            throw new CustomerException(Constants.PARAM_ERROR, "请选择合适的类型查询");
        }

        // 封装page
        page.setRecords(articleVOList);
        page.setTotal(count);

        // 返回结果
        return page;
    }

    /**
     * 查询文章作者
     *
     * @param articleId 文章id
     * @return 作者信息
     */
    @Override
    public UserVO searchAuthorByArticleId(Long articleId) {
        // 获取userId
        long author = articleMapper.selectAuthorById(articleId);

        // 查询作者
        BaseResponse<UserVO> res = userClient.getById(author);
        if (res.getCode() != 1)
            throw new CustomerException(Constants.PARAM_ERROR, "未查询到作者");

        return res.getData();
    }

    /**
     * 后台管理-分页查询
     *
     * @param articleParam 文章查询参数
     * @return ArticleVO分页数据
     */
    @Override
    public IPage<Article> searchManageArticlePage(QueryManageArticleParam articleParam) {
        // 设置pastDate
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE, -articleParam.getPastDay());
        Date time = calendar.getTime();
        Timestamp timestamp = new Timestamp(time.getTime());
        articleParam.setPastDate(timestamp);

        // 构造分页构造器
        IPage<Article> page = new Page<>(articleParam.getPageParam().getPageNum(),
                articleParam.getPageParam().getPageSize());
        // 构造查询构造器
        String title = articleParam.getTitle();
        Integer type = articleParam.getType();
        Integer status = articleParam.getStatus();
        Date pastDay = articleParam.getPastDate();
        LambdaQueryWrapper<Article> pageWrapper = Wrappers.lambdaQuery(Article.class)
                .like(StrUtil.isNotBlank(title), Article::getTitle, title)
                .eq(BeanUtil.isNotEmpty(type), Article::getType, type)
                .eq(BeanUtil.isNotEmpty(status), Article::getArticleStatus, status)
                .between(BeanUtil.isNotEmpty(pastDay), Article::getCreateTime, pastDay, new Date())
                .orderByDesc(Article::getCreateTime);
        // 分页查询
        articleMapper.selectPage(page, pageWrapper);

        return page;
    }

    /**
     * 修改文章状态
     *
     * @param statusParam 文章状态参数
     * @return 提示信息
     */
    @Transactional
    @Override
    public String updateStatus(ArticleStatusParam statusParam) {
        try {
            // 修改文章状态
            Timestamp timestamp = new Timestamp(new Date().getTime());
            articleMapper.updateStatusById(statusParam.getId(), statusParam.getStatus(), timestamp);
        } catch (Exception e) {
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }
        return "修改成功";
    }

    /**
     * 保存文章信息
     *
     * @param saveArticleParam 文章参数
     * @return 提示信息
     */
    @Override
    public String saveArticle(SaveArticleParam saveArticleParam) {
        // 获取当前认证的id
        Long loginId = Long.parseLong(StpUtil.getLoginId().toString());

        // 对作者进行二次校验
        Long author = saveArticleParam.getUserId(); // 获取作者id
        matchUser(author, loginId);

        // 对创建人进行二次校验
        Long createBy = saveArticleParam.getCreateBy();
        matchUser(createBy, loginId);

        // 对修改人进行二次校验
        Long updateBy = saveArticleParam.getUpdateBy();
        matchUser(updateBy, loginId);

        // 对象文章标签id进行二次校验
        List<Long> tagIdList = saveArticleParam.getTagIdList();
        matchTag(tagIdList);

        // 手动开启新增事务
        DefaultTransactionDefinition insertDefinition = new DefaultTransactionDefinition();
        insertDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 传播行为PROPAGATION_REQUIRED
        TransactionStatus insertTransaction = transactionManager.getTransaction(insertDefinition);

        Article article = null;
        Long articleId = null;
        try {
            article = new Article();
            BeanUtil.copyProperties(saveArticleParam, article, "tagIdList");
            // 保存文章
            article.setArticleStatus(-1); // 审核未通过
            this.saveOrUpdate(article);
            articleId = article.getId();

            // 提交事务
            transactionManager.commit(insertTransaction);

            // 判断文章是否转载，转载无需相似度校验
            if (article.getType() != 1) {
                // 检查相似度是否合格
                if (!calculateSimilarity(articleId)) {
                    return "文章相似度达40%以上，请重新更改";
                }
            }
        } catch (Exception e) {
            transactionManager.rollback(insertTransaction);
        }

        // 手动开启脱敏事务
        DefaultTransactionDefinition sensitiveDefinition = new DefaultTransactionDefinition();
        sensitiveDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus sensitiveTransaction = transactionManager.getTransaction(sensitiveDefinition);
        // 敏感信息屏蔽
        try {
            List<ArticleTag> articleTagList;
            // 查询原始对象
            Article entity = this.getById(articleId);
            BeanUtil.copyProperties(article, entity, CopyOptions.create().ignoreNullValue());

            // 标题、简介脱敏
            entity.setTitle(maskSensitiveWords(articleId, SENSITIVE_TITLE)); // 标题
            entity.setDescription(maskSensitiveWords(articleId, SENSITIVE_DESCRIPTION)); // 简介

            // 内容、图片检测
            entity = maskSensitiveConetntAndImg(entity);

            // 有id，修改文章信息
            entity.setUpdateTime(new Timestamp(new Date().getTime()));
            // 修改审核状态
            entity.setArticleStatus(1);
            articleMapper.updateById(entity); // 修改文章基本信息
            // 构造查询条件
            LambdaQueryWrapper<ArticleTag> deleteTagWrapper = Wrappers.lambdaQuery(ArticleTag.class)
                    .eq(BeanUtil.isNotEmpty(articleId), ArticleTag::getArticleId, articleId);
            articleTagMapper.delete(deleteTagWrapper);
            articleTagList = getArticleTagListByTagIds(createBy, updateBy, tagIdList, article);
            // 批量保存articleTagList
            articleTagMapper.insertBatch(articleTagList);

            // 提交事务
            transactionManager.commit(sensitiveTransaction);
        } catch (Exception e) {
            transactionManager.rollback(sensitiveTransaction);
            throw new CustomerException(ErrorCode.SYSTEM_ERROR);
        }

        // 返回提示信息
        return "保存成功";
    }


}




